/*************************************************************************
    > File Name: http.c
    > Author: 侯广臣
    > Mail: 2801059691@qq.com 
    > Created Time: 2021年01月02日 星期六 16时31分38秒
 ************************************************************************/

#include "http.h"

//从http客户端的请求中解析出method : 请求方法，URL，如果version参数不为NULL则解析出Http版本信息
//错误返回 -1
int RequestInfo(const char *requestBuf, char *method, char *url, char *version)
{
	if(requestBuf == NULL || method == NULL || url == NULL)
	{
		return -1;
	}

	if(version == NULL)
	{
		return sscanf(requestBuf, "%s %s", method, url);
	}
	else
	{
		return sscanf(requestBuf, "%s %s %s", method, url, version);
	}
}

//返回请求所代表的整数值，取值范围为 enum NETHOD 所包含类型
int MethodNumber(const char *method)
{
	int methodNumber = 0;
	switch(method[0])
	{
		case 'G':
			if(strcmp(method, "GET") == 0)
				methodNumber = METHOD_GET;
			break;
		case 'H':
			if(strcmp(method, "HEAD") == 0)
				methodNumber = METHOD_HEAD;
			break;
		case 'O':
			if(strcmp(method, "OPTIONS") == 0)
				methodNumber = METHOD_OPTIONS;
			break;
		case 'D':
			if(strcmp(method, "DELETE") == 0)
				methodNumber = METHOD_DELETE;
			break;
		case 'T':
			if(strcmp(method, "TRACE") == 0)
				methodNumber = METHOD_TRACE;
			break;
		case 'C':
			if(strcmp(method, "CONNECT") == 0)
				methodNumber = METHOD_CONNECT;
			break;
		case 'P':
			switch(method[1])
			{
				case 'O':
					if(strcmp(method, "POST") == 0)
						methodNumber = METHOD_POST;
					break;
				case 'U':
					if(strcmp(method, "PUT") == 0)
						methodNumber = METHOD_PUT;
					break;
				case 'A':
					if(strcmp(method, "METHOD_PATCH") == 0)
						methodNumber = METHOD_PATCH;
					break;
			}
			break;
		default: methodNumber = 0; break;
	}

	return methodNumber;
}

//如果解析后请求为错误，则调用标准请求错误，sockfd: 客户端套接字
//返回发送的字节数，如果错误返回 -1
int SendBadRequest(int sockfd)
{
	char stateLine[] = "HTTP/1.0 400 Bad Request\r\n";	//状态行
	char serverName[] = "Server: Linux Web Server\r\n";	//服务器名
	char contentType[] = "Content_Type: text/html\r\n\r\n";	//内容类型
	char content[] = 
		"<html><br><head><title>400 Bad Request</title></head><br><body><h1>Request Error!</h1></body></html>";
	//集中写结构体
	struct iovec iov[4];
	//初始化集中写结构体
	char* array[4] = {stateLine, serverName, contentType, content};
	for(int i = 0; i < 4; i++)
	{
		iov[i].iov_base = array[i];
		iov[i].iov_len = strlen(array[i]);
	}
	
	return writev(sockfd, iov, 4);
}

//解析正确后发送的响应头，sockfd: 客户端套接字 contentSzie: 响应体字节数 contentType: 响应体类型
//返回发送的字节数，如果错误返回 -1
int SendResponseHead(int sockfd, int contentSize, const char *contentType)
{
	char contentHead[256];
	int len = sprintf(contentHead,
	"CHTTP/1.0 200 OK\r\nServer: Linux Web Server\r\ncontent-Type: %s; charset=UTF-8\r\nContent-Length: %d\r\n\r\n",
			contentType, contentSize);

	return write(sockfd, contentHead, len);
}

//发送响应体，sockfd: 客户端套接字 fileName: 需要发送给客户端的文件
//返回发送的字节数，如果错误返回 -1
int SendResponseContent(int sockfd, const char *fileName)
{
	if(fileName == NULL	|| access(fileName, F_OK) != 0)
	{
		return -1;
	}

	int fd = open(fileName, O_RDONLY);
	if(fd == -1)
	{
		return -1;
	}

	struct stat fileStat;
	if(stat(fileName, &fileStat) != 0)
	{
		close(fd);
		return -1;
	}
	
	int sendLen = sendfile(sockfd, fd, NULL, fileStat.st_size);
	close(fd);

	return sendLen;
}


//发送Get请求的回应，错误反胃 -1
int SendGetResponse(int sockfd, const char *fileName)
{
	if(sockfd < 0 || fileName == NULL)
	{
		return -1;
	}

	if(access(fileName, F_OK) != 0)
	{
		return SendBadRequest(sockfd);
	}
	int fd = open(fileName, O_RDONLY);
	if(fd == -1)
	{
		return -1;
	}

	struct stat fileStat;
	if(stat(fileName, &fileStat) == -1)
	{
		close(fd);
		return -1;
	}

	int sendHeadLen = SendResponseHead(sockfd, fileStat.st_size, "text/html");
	if(sendHeadLen <= 0)
	{
		close(fd);
		return -1;
	}
	
	int sendContentLen = SendResponseContent(sockfd, fileName);
	if(sendContentLen <= 0)
	{
		close(fd);
		return -1;
	}
	
	close(fd);
	return sendHeadLen + sendContentLen;
}

//把字符串str1, str2连接填充到catString中
//返回填充好的字符串，错误返回 NULL
char* StringCat(char catString[], const char *str1, const char *str2)
{
	if(strcpy(catString, str1) == NULL)
	{
		return NULL;
	}

	if(strcat(catString, str2) == NULL)
	{
		return NULL;
	}

	return catString;
}
